/*
 * WorkMtrApi.cpp
 *
 *  Created on: 2017-1-24
 *      Author: root
 */

#include"WorkMtrApi.h"
#if USE_PUBLIC_MTR
#include"PublicLog.h"
#include"PublicRandom.h"
#include"WorkConnectApi.h"
#include<stdio.h>
#include<stdlib.h>
#include"PublicSocket.h"
#include"PublicNetHead.h"
#include<sys/epoll.h>
#include<errno.h>
#define USE_NFT_POPEN 1
#if USE_NFT_POPEN
extern "C"{
#include"nft_popen.h"
};
#else
#include"PublicPopen.h"
#endif
std::string GetMtrInfo(const char*IP){
    char common[1024]={0};
    std::string info;
    sprintf(common,"mtr -r -n -c 10 %s ",IP);
    info=common;
    info+="\n";
    FILE*fp=popen(common,"r");
    if(fp){
		do{
			char buff[1024]={0};
			int ret=fread(buff,1,1000,fp);
			if(ret>0){
				info.append(buff);
			}else{
				break;
			}
		}while(1);
		pclose(fp);
    }
    return info;
}
typedef struct _FILEINFO{
int	fd;	
FILE*fp;
pid_t pid;
time_t timestamp;
}FILEINFO;
typedef std::map<std::string,FILEINFO>FILEINFOLIST;
//-1失败 0正常
int MyAddSocketMemForMtr(int epoll_sock,int fd,struct in_addr sin_addr,u_short port){
    struct epoll_event event={0};
    epoll_data_ex data;
    memset(&data,0,sizeof(epoll_data_ex));
//设置异步
#if 0
	struct timeval  tv={1,0};
	if(setsockopt(fd,SOL_SOCKET,SO_RCVTIMEO,(char*)&tv,sizeof(tv))!=0){
		WRONG("setsockopt SO_RCVTIMEO err");
    }
#else	
	if(fcntl(fd,F_SETFL,fcntl(fd,F_GETFL)|O_NONBLOCK)!=0){
		WRONG("setsockopt O_NONBLOCK err");
    }
#endif	
    data.data.fd=(int16_t)fd;
    data.data.port=port;
    data.data.addr=sin_addr.s_addr;
    event.data.u64=data.u64;
	event.events=EPOLLIN;
    //event.events=EPOLLIN|EPOLLONESHOT;
    return epoll_ctl(epoll_sock,EPOLL_CTL_ADD,fd,&event);
}
int MyDelSocketMemForMtr(int epoll_sock,int fd){
	return epoll_ctl(epoll_sock,EPOLL_CTL_DEL,fd,NULL);
}

MAPSTRINGLIST GetMtrInfo(SETIPLIST IPList,size_t MaxWaitList){
    MAPSTRINGLIST result;
    int epoll_sock=epoll_create(MaxWaitList);
    if(epoll_sock==-1){
		WRONG("Create epoll_sock Error");
		return result;
    }
    struct epoll_event*events=(struct epoll_event*)malloc(sizeof(struct epoll_event)*MaxWaitList);
    if(events==NULL){
		close(epoll_sock);
		WRONG("Create Events Error");
		return result;
    }
    size_t totalnum=IPList.size();
	size_t checknum=0;
    FILEINFOLIST waitlist;
	time_t checktime=time(NULL);
    PublicRandom TmpList;
    TmpList.InsertSetList(IPList);
    while((checknum<totalnum)&&ChekcEnablePublicNet()){
		if((TmpList.empty()==false)&&(waitlist.size()<MaxWaitList)){
			std::string ip=TmpList.PopList();
			char common[1024]={0};
			sprintf(common,"mtr -r -n -c 10 %s ",ip.c_str());
			ReSetStartTime();
//2.6.32会立刻返回
//2.6.9会卡住
			FILEINFO tmpinfo={-1,NULL,0,0};
			#if USE_NFT_POPEN
			tmpinfo.fd=nft_popen(common,"r");
			#else
			tmpinfo.fp=PublicPopen(common,"r",&tmpinfo.pid);
			tmpinfo.fd=fileno(tmpinfo.fp);
			#endif
			INFO("popen打开耗时:%d ms pid:%d fd:%d ip:%s",GetEndTime(),tmpinfo.pid,tmpinfo.fd,ip.c_str());
			if(tmpinfo.fd!=-1){
				tmpinfo.timestamp=time(NULL);
				waitlist.insert(std::pair<std::string,FILEINFO>(ip.c_str(),tmpinfo));
				in_addr_t addr=inet_addr(ip.c_str());
				struct in_addr sin_addr;
				sin_addr.s_addr=addr;
				MyAddSocketMemForMtr(epoll_sock,tmpinfo.fd,sin_addr,0);
			}else{
				WRONG("打开文件失败,common:%s",common);
				TmpList.PushList(ip);
            }
        }
//定时接收一次队列
		memset(events,0,sizeof(struct epoll_event)*MaxWaitList);
		ReSetStartTime();
		int n=epoll_wait(epoll_sock,events,MaxWaitList,1000);
		//INFO("epoll耗时:%d 返回数量:%d",GetEndTime(),n);
		int i=0;
		for(i=0;i<n;i++){
			epoll_data_ex*ptr=(epoll_data_ex*)&events[i].data;
			char IP[40]={0};
			GetIPByHostID(IP,ptr->data.addr);
			FILEINFOLIST::iterator it=waitlist.find(IP);
			if(it!=waitlist.end()){
				char buff[1024]={0};
				ReSetStartTime();
				//int ret=fread(buff,1,512,it->second.fp);
				int ret=read(ptr->data.fd,buff,512);
				INFO("read:耗时:%d ms Ret:%d ",GetEndTime(),ret);
				if(ret>0){
					MAPSTRINGLIST::iterator ittmp=result.find(IP);
					if(ittmp!=result.end()){
						ittmp->second.append(buff);
					}else{
						result.insert(std::pair<std::string,std::string>(IP,buff));
					}
				}else{
					WARN("erase %s",IP);
					MyDelSocketMemForMtr(epoll_sock,ptr->data.fd);
#if USE_NFT_POPEN					
					nft_pclose(ptr->data.fd);
#else
					PublicPclose(it->second.fp,it->second.pid);
#endif					
					waitlist.erase(IP);
					checknum++;
				}
			}else{
//出现代码就有问题				
				WRONG("IP:%s不存在",IP);
//同时从队列中移除该监听项			
				MyDelSocketMemForMtr(epoll_sock,ptr->data.fd);
			}
        }
//检查一次超时
		time_t now=time(NULL);
		if((now-checktime)>2){
			FILEINFOLIST::iterator it=waitlist.begin();
			time_t now=time(NULL);
			PublicRandom tmpsave;
			while(it!=waitlist.end()){
				if((now-it->second.timestamp)>5){
					MAPSTRINGLIST::iterator ittmp=result.find(it->first);
					if(ittmp!=result.end()){
						ittmp->second.append("\n执行超时");
					}else{
						result.insert(std::pair<std::string,std::string>(it->first,"\n执行超时"));
					}
//及时关闭否则还在			
					MyDelSocketMemForMtr(epoll_sock,it->second.fd);
#if USE_NFT_POPEN
					nft_pclose(it->second.fd);
#else					
					PublicPcloseShutdown(it->second.fp,it->second.pid);
#endif					
					checknum++;
					tmpsave.PushList(it->first);
				}
				it++;
			}
			INFO("检查一次超时: 本次发现超时数量:%zd",tmpsave.size());
			while(tmpsave.Empty()==false){
				waitlist.erase(tmpsave.PopList());
			}
			checktime=now;
		}while(0);
    }
    FILEINFOLIST::iterator it=waitlist.begin();
    while(it!=waitlist.end()){
#if USE_NFT_POPEN		
		nft_pclose(it->second.fd);
#else		
		PublicPcloseShutdown(it->second.fp,it->second.pid);
#endif		
		it++;
    }
    close(epoll_sock);
    if(events)free(events);
    return result;
}
#endif
